﻿/**
 *@description: 普普通通纹理渲染类
 *@file: common_renderer.h
 *@author: snow-tyan
 *@email: zziywang@163.com
 *@date: 8 February 2023
 *@copyright: 2018-2022 AiRiA. All rights reserved.
 */

#pragma once

#include "mygl/es_image.h"
#include "mygl/shader.h"
#include "mygl/texture2d.h"
#include "mygl/yuv_texture2d.h"

#include <array>
#include <glad/glad.h>

namespace mygl {

class Shader;
class Texture2D;
class YuvTexture2D;

// 静态渲染，不会更新顶点
// ------------------------------
class StaticRenderer {
 public:
  StaticRenderer(std::shared_ptr<Shader> shader, GLuint width, GLuint height);
  ~StaticRenderer();
  /**
   * @brief 朴素的纹理渲染
   * @param  texture2d: Texture2D类
   * @param  position: 纹理位置
   * @param  size: 纹理大小
   * @param  rotate: 旋转角度
   * @param  color: 纹理颜色
   */
  void Render(std::shared_ptr<Texture2D> texture2d,
              glm::vec2 position = glm::vec2(0.0f, 0.0f),
              glm::vec2 size = glm::vec2(10, 10), glm::vec3 color = glm::vec3(1.0f));
  /**
   * @brief 渲染2个Texture，伴随着转场动画
   * @param  texture2d_1: Texture2D类A
   * @param  texture2d_2: Texture2D类B
   * @param  progress: 转场进度，0.0表示完全是纹理A，1.0表示完全是纹理B
   * @param  position: 纹理位置
   * @param  size: 纹理大小
   * @param  color: 纹理颜色
   */
  void RenderWithTransition(std::shared_ptr<Texture2D> texture2d_1,
                            std::shared_ptr<Texture2D> texture2d_2, GLfloat progress,
                            glm::vec2 position = glm::vec2(0.0f, 0.0f),
                            glm::vec2 size = glm::vec2(800.0f, 600.0f),
                            glm::vec3 color = glm::vec3(1.0f));
  /**
   * @brief 渲染多个Texture，伴随着转场动画
   * @param  texture2ds: 多个Texture的指针数组
   * @param  progress: 转场进度，0.0表示完全是纹理A，1.0表示完全是纹理B
   * @param  position: 纹理位置
   * @param  size: 纹理大小
   * @param  color: 纹理颜色
   */
  void RenderMultiTextureWithTransition(
      std::vector<std::shared_ptr<Texture2D>> texture2ds, GLfloat progress,
      glm::vec2 position = glm::vec2(0.0f, 0.0f),
      glm::vec2 size = glm::vec2(800.0f, 600.0f), glm::vec3 color = glm::vec3(1.0f));
  /**
   * @brief 渲染Yuv420p原始图像，一帧
   * @param  texture2d: YuvTexture2D类，由ResourceManager管理
   * @param  format: 图像格式
   * @param  position: 纹理位置
   * @param  size: 纹理大小
   * @param  color: 纹理颜色
   */
  void RenderYuv(std::shared_ptr<YuvTexture2D> yuv_texture2d,
                 ImageFormat format = ImageFormat::YUV420SP_NV21,
                 glm::vec2 position = glm::vec2(0.0f, 0.0f),
                 glm::vec2 size = glm::vec2(10, 10), glm::vec3 color = glm::vec3(1.0f));
  void RenderYuv(GLuint y_texture, GLuint u_texture, GLuint v_texture,
                 glm::vec2 position = glm::vec2(0.0f, 0.0f),
                 glm::vec2 size = glm::vec2(10, 10), glm::vec3 color = glm::vec3(1.0f));
  /**
   * @brief 渲染Yuv420p原始视频
   * @param  yuv_texture2d: YuvTexture2D类，由ResourceManager管理
   * @param  frame: 当前帧
   * @param  format: 图像格式
   * @param  position: 纹理位置
   * @param  size: 纹理大小
   * @param  color: 纹理颜色
   */
  void RenderYuvVideo(std::shared_ptr<YuvTexture2D> yuv_texture2d, size_t frame,
                      ImageFormat format = ImageFormat::YUV420SP_NV21,
                      glm::vec2 position = glm::vec2(0.0f, 0.0f),
                      glm::vec2 size = glm::vec2(800.0f, 600.0f),
                      glm::vec3 color = glm::vec3(1.0f));
  // 更底层的接口
  void RenderYuvVideo(GLuint y_texture, GLuint uv_texture, GLuint frame_width,
                      GLuint frame_height, const std::vector<uint8_t> &y_data,
                      const std::vector<uint8_t> &uv_data,
                      ImageFormat format = ImageFormat::YUV420SP_NV21,
                      glm::vec2 position = glm::vec2(0.0f, 0.0f),
                      glm::vec2 size = glm::vec2(800.0f, 600.0f),
                      glm::vec3 color = glm::vec3(1.0f));
  /**
   * @brief 渲染RGB888原始视频
   * @param  texture2d: Texture2D类，由ResourceManager管理
   * @param  frame: 当前帧
   * @param  format: 图像格式
   * @param  position: 纹理位置
   * @param  size: 纹理大小
   * @param  color: 纹理颜色
   */
  void RenderVideo(std::shared_ptr<Texture2D> texture2d, GLuint frame,
                   ImageFormat format = ImageFormat::RGB,
                   glm::vec2 position = glm::vec2(0.0f, 0.0f),
                   glm::vec2 size = glm::vec2(800.0f, 600.0f),
                   glm::vec3 color = glm::vec3(1.0f));
  void RenderVideo(std::shared_ptr<Texture2D> texture2d, GLuint frame, double fps,
                   ImageFormat format = ImageFormat::RGB,
                   glm::vec2 position = glm::vec2(0.0f, 0.0f),
                   glm::vec2 size = glm::vec2(800.0f, 600.0f),
                   glm::vec3 color = glm::vec3(1.0f));
  // 更底层的接口
  void RenderVideo(GLuint texture, GLuint frame_width, GLuint frame_height,
                   const std::vector<uint8_t> &data,
                   ImageFormat format = ImageFormat::RGB,
                   glm::vec2 position = glm::vec2(0.0f, 0.0f),
                   glm::vec2 size = glm::vec2(800.0f, 600.0f),
                   glm::vec3 color = glm::vec3(1.0f));

 private:
  void initRenderData();

 private:
  GLuint vao_;
  std::shared_ptr<Shader> shader_;
};

struct Box {
  GLuint x, y, w, h;

  Box(GLuint x = 0, GLuint y = 0, GLuint w = 0, GLuint h = 0) : x(x), y(y), w(w), h(w) {}
  Box operator+(const Box &rhs) {
    return Box(x + rhs.x, y + rhs.y, w + rhs.w, h + rhs.h);
  }
};
/// TODO:
// 动态渲染会动态更新VBO
// -----------------------------------
class DynamicRenderer {
 public:
  DynamicRenderer(std::shared_ptr<Shader> shader, GLuint width, GLuint height)
      : vao_(0), vbo_(0), shader_(shader) {
    glm::mat4 projection = glm::ortho(0.0f, static_cast<float>(width),
                                      static_cast<float>(height), 0.0f, -1.0f, 1.0f);
    shader_->UseProgram();
    shader_->SetUniformMat4Float("projection", projection);
    initRenderData();
  }
  ~DynamicRenderer() {
    glDeleteVertexArrays(1, &vao_);
    glDeleteBuffers(1, &vbo_);
  }

  void Render(std::shared_ptr<Texture2D> texture2d, Box box,  // 原图框
              GLuint image_width, GLuint image_height,        // 原图宽高
              glm::vec2 position = glm::vec2(0.0f, 0.0f),     // 纹理位置
              glm::vec2 size = glm::vec2(800.0f, 600.0f),     // 纹理尺寸
              glm::vec3 color = glm::vec3(1.0f)) {            // 纹理颜色
    // transfrom
    glm::mat4 model(1.0f);
    model = glm::translate(model, glm::vec3(position, 0.0f));
    // 不做旋转，仅做位移和缩放
    model = glm::scale(model, glm::vec3(size, 1.0f));
    shader_->UseProgram();
    shader_->SetUniformMat4Float("model", model);
    shader_->SetUniformVec3Float("tex_color", color);

    glActiveTexture(GL_TEXTURE0);
    texture2d->Bind();
    glm::vec2 tex_scale = glm::vec2(1.0f / image_width, 1.0f / image_height);
    auto update_xywh = scale(box, tex_scale);
    // 更新vbo
    // clang-format off
    GLfloat vertices[] = {
        1.0f, 1.0f, update_xywh[0] + update_xywh[2], update_xywh[1] + update_xywh[3], // 右上 0
        1.0f, 0.0f, update_xywh[0] + update_xywh[2], update_xywh[1], // 右下 1
        0.0f, 0.0f, update_xywh[0], update_xywh[1], // 左下 2
        0.0f, 1.0f, update_xywh[0], update_xywh[1] + update_xywh[3], // 左上 3
    };
    // clang-format on
    glBindBuffer(GL_ARRAY_BUFFER, vbo_);
    glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(vertices), vertices);
    glBindBuffer(GL_ARRAY_BUFFER, GL_NONE);
    glBindVertexArray(vao_);
    glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, 0);
    // glBindVertexArray(GL_NONE);  // Unbind
  }

 private:
  std::array<float, 4> scale(Box src_box, glm::vec2 ratio) {
    std::array<float, 4> dst_box = {0};
    dst_box[0] = src_box.x * ratio.x;
    dst_box[1] = src_box.y * ratio.y;
    dst_box[2] = src_box.w * ratio.x;
    dst_box[3] = src_box.h * ratio.y;
    return dst_box;
  }
  void initRenderData() {
    GLfloat vertices[] = {
        1.0f, 1.0f, 1.0f, 1.0f,  // 右上 0
        1.0f, 0.0f, 1.0f, 0.0f,  // 右下 1
        0.0f, 0.0f, 0.0f, 0.0f,  // 左下 2
        0.0f, 1.0f, 0.0f, 1.0f,  // 左上 3
    };
    GLshort indices[] = {
        0, 1, 3,  //
        1, 2, 3,
    };
    GLuint ebo;
    glGenVertexArrays(1, &vao_);
    glGenBuffers(1, &vbo_);
    glGenBuffers(1, &ebo);

    glBindVertexArray(vao_);
    glBindBuffer(GL_ARRAY_BUFFER, vbo_);
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_DYNAMIC_DRAW);

    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_DYNAMIC_DRAW);

    glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 4 * sizeof(GLfloat), (const void *)0);
    glEnableVertexAttribArray(0);
    // 必须先Unbind VAO 否则会崩 因为VAO中存很多属性和索引数组
    glBindVertexArray(GL_NONE);
    glBindBuffer(GL_ARRAY_BUFFER, GL_NONE);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, GL_NONE);
  }

 private:
  GLuint vao_;
  GLuint vbo_;
  std::shared_ptr<Shader> shader_;
};

}  // namespace mygl